﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Web.Services.Description;
using System.Net;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Diagnostics;
using System.Security.Permissions;
using System.Security;
using System.Windows;

namespace WSDLFlattener
{
    public class MainWindowController
    {
        /// <summary>
        /// Flattens the WSDL.
        /// </summary>
        /// <param name="wsdlUri">The WSDL URI.</param>
        /// <param name="namespacesToRemove">The namespaces to remove.</param>
        internal void FlattenWSDL(string wsdlUri, List<string> namespacesToRemove)
        {
            FileIOPermission filePermission = new FileIOPermission(FileIOPermissionAccess.Write, Path.GetTempPath());
            PermissionSet ps = new PermissionSet(PermissionState.Unrestricted);
            ps.AddPermission(filePermission);

            try
            {
                ps.Demand();

                ServiceDescription serviceDescription = GetServiceFromURI(wsdlUri);

                var schemasToImport = new Dictionary<string, XmlSchema>();

                // TODO: validate against repeated namespaces
                AddImportedSchemas(serviceDescription, schemasToImport);

                foreach (XmlSchema schema in schemasToImport.Values)
                    AddImportedSchemas(serviceDescription, schema, namespacesToRemove);

                serviceDescription.Imports.Clear();

                string tempPath = Path.GetTempFileName() + ".txt";
                serviceDescription.Write(tempPath);
                Process.Start(tempPath); // this will trigger the opening of the file with notepad (or other associated app)
            }
            catch (SecurityException)
            {
                MessageBox.Show("This applications needs permissions to write to temporary folder");
            }
            catch (InvalidOperationException)
            {
                MessageBox.Show("Error retrieving target WSDL");
            }
        }

        /// <summary>
        /// Adds the imported schemas.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="schema">The schema.</param>
        /// <param name="namespacesToRemove">The namespaces to remove.</param>
        private void AddImportedSchemas(ServiceDescription serviceDescription, XmlSchema schema, List<string> namespacesToRemove)
        {
            foreach (XmlSchemaImport schemaImport in schema.Includes)
            {
                XmlSchema importedSchema = GetSchemaFromURI(schemaImport.SchemaLocation);
                AddImportedSchemas(serviceDescription, importedSchema, namespacesToRemove);
            }

            schema.Includes.Clear();
            if(schema.Items.Count>0 && !namespacesToRemove.Contains(schema.TargetNamespace))
                serviceDescription.Types.Schemas.Add(schema);
        }

        /// <summary>
        /// Adds the imported schemas.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="schemasToImport">The schemas to import.</param>
        private void AddImportedSchemas(ServiceDescription serviceDescription, Dictionary<string, XmlSchema> schemasToImport)
        {
            foreach (XmlSchema schema in serviceDescription.Types.Schemas)
            {
                if (!schemasToImport.ContainsKey(schema.TargetNamespace))
                    schemasToImport[schema.TargetNamespace] = schema;
            }

            if (serviceDescription.Imports.Count > 0)
            {
                foreach (Import import in serviceDescription.Imports)
                    AddImportedSchemas(GetServiceFromURI(import.Location), schemasToImport);
            }
        }

        /// <summary>
        /// Loads the ServiceDescription from the supplied URI.
        /// </summary>
        /// <param name="uri">The URI.</param>
        /// <returns>An instance of a ServiceDescription</returns>
        private ServiceDescription GetServiceFromURI(string uri)
        {
            WebRequest webRequest = WebRequest.Create(uri);
            WebResponse webResponse = webRequest.GetResponse();
            ServiceDescription serviceDescription = ServiceDescription.Read(webResponse.GetResponseStream());

            return serviceDescription;
        }

        /// <summary>
        /// Loads an XmlSchema from the supplied URI.
        /// </summary>
        /// <param name="uri">The URI.</param>
        /// <returns>An instance of an XmlSchema</returns>
        private XmlSchema GetSchemaFromURI(string uri)
        {
            WebRequest webRequest = WebRequest.Create(uri);
            WebResponse webResponse = webRequest.GetResponse();
            XmlSchema schema = XmlSchema.Read(webResponse.GetResponseStream(), null);

            return schema;
        }
    }
}
